package at.ac.tuwien.dse.health.event.bean;

import at.ac.tuwien.dse.health.event.bus.EventBus;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.util.PatternMatchUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;

/**
 * Processes beans using the eventing system ensuring that their are initialized properly in order to send events to
 * subscribers or receive events of potential interest from other event sources.
 * <p/>
 * For every {@link EventBus} used by any bean, the source of the bus must be provided. If it is not, the declaring
 * object becomes the source of the {@link EventBus} instance.
 *
 * @author Bernhard Nickel
 * @author Gregor Schauer
 * @author Dominik Strasser
 */
public class EventingBeanPostProcessor implements BeanPostProcessor {
	protected String[] beanNames;
	protected EventBus observingEventBus;
	protected EventBus consumingEventBus;

	@Override
	public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
		ReflectionUtils.doWithFields(bean.getClass(), new ReflectionUtils.FieldCallback() {
					@Override
					public void doWith(Field field) {
						ReflectionUtils.makeAccessible(field);
						EventBus eventBus = (EventBus) ReflectionUtils.getField(field, bean);
						if (eventBus != null && eventBus.getSource() == null) {
							eventBus.setSource(bean.getClass().getName());
						}
					}
				}, new ReflectionUtils.FieldFilter() {
					@Override
					public boolean matches(Field field) {
						return EventBus.class.isAssignableFrom(field.getType());
					}
				}
		);

		if (PatternMatchUtils.simpleMatch(beanNames, beanName)) {
			if (observingEventBus != null) {
				observingEventBus.register(bean, true);
			}
			if (consumingEventBus != null) {
				consumingEventBus.register(bean, false);
			}
		}

		return bean;
	}

	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		return bean;
	}

	public void setObservingEventBus(EventBus observingEventBus) {
		this.observingEventBus = observingEventBus;
	}

	public void setConsumingEventBus(EventBus consumingEventBus) {
		this.consumingEventBus = consumingEventBus;
	}

	public void setBeanNames(String... beanNames) {
		this.beanNames = beanNames;
	}
}
